#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File    :   DTC_test.py
@Time    :   2025/03/19 13:31:28
@Author  :   YangLiang 
@Version :   1.0
@Contact :   yangl@genvict.com
@License :   (C)Copyright 2022-2025
@Desc    :   None
'''

# here put the import lib
from cgitb import enable
from email.message import Message
from pytest_assume.plugin import assume
 
import pytest
import binascii
import logging
import time
import requests
from sympy import I
# from sympy import assuming
from commDriver.TS_CAN import *
import allure
import sys
from collections.abc import Iterable

from sqlalchemy import false
sys.dont_write_bytecode = True
import Set_up


@allure.severity('critical')
class Test_DTC_Phy(object):

    def dtc_time_check(self,Driver,ex_dtc_list,timeout=5):
        '''
        输入故障码和期望状态
        循环请求 19服务,当读到dtc码或超时5s仍然无DTC码时中断
        '''
        global total_time
        flag = True
        time_start = time.time()
        while flag:
            if time.time() - time_start <= timeout :
                uds_rep,size = Driver['UDS'].req_and_res_can([0x19,0x02,0x09],3,enable_print = False)
                act_dtc_list = []
                if len(uds_rep) >= 7:
                    for i in range(int((len(uds_rep)-3)/4)):
                        act_dtc_list = uds_rep[3+i:7+i]
                        i += 4
                        if set(ex_dtc_list).issubset(set(act_dtc_list)):
                            time_end = time.time()
                            total_time = round(time_end - time_start, 3)
                            readstr = binascii.b2a_hex(bytearray(uds_rep[3:]),' ')
                            logging.info('total time cost :{0} s ---,DTC_status<<<{1}>>>\n'.format(str(total_time),readstr))
                            flag = False
            else:
                flag = False
                time_end = time.time()
                total_time = round(time_end - time_start, 3)
                logging.warning('Overtime, No DTC code output!')
                with assume: assert total_time < timeout 
        return total_time


    
    def clearDTC(self, Driver, iStep,checkinitialized=True):
        # 清除DTC故障
        iStep,uds_resp,size = Driver['CAN'].checkReqResp(Driver, [0x14,0xFF,0xFF,0xFF], [0x54], 0, iStep, False, 'UDS', 'noClearFault')
        time.sleep(2)
        if checkinitialized:
            iStep,uds_resp,size = Driver['CAN'].checkReqResp(Driver, [0x19,0x02,0x09], [0x59,0x02,0x09], 0, iStep, False, 'UDS', 'noClearFault')
            iStep = Driver['CAN'].delInitialFailure(iStep,uds_resp)
        return iStep

    def searchDTC(self, Driver,req, DTC_Number,iStep, isTrue, MatureConditionsTime=1000, timeout=0.5):
        # 查询故障
        with allure.step(f"Step{iStep}: search DTC {binascii.b2a_hex(bytearray(DTC_Number), ' ')}"): # type: ignore
            time.sleep(MatureConditionsTime/1000)
            a,b,c=Driver['UDS'].search_DTC(req, DTC_Number, timeout)
            if isTrue:
                if (b + MatureConditionsTime/1000) > (MatureConditionsTime/1000 + 0.15):
                    logging.error(f"MatureConditionsTime = {MatureConditionsTime/1000 + 0.1}, realtime = {b + MatureConditionsTime/1000}")
                if a != True:
                    logging.error(f"No found DTC{binascii.b2a_hex(bytearray(DTC_Number), ' ')}")
                with assume: assert a == True and (b + MatureConditionsTime/1000) <= (MatureConditionsTime/1000 + 0.1)
                iStep,uds_resp,size = Driver['CAN'].checkReqResp(Driver, [0x19, 0x04] + DTC_Number + [0x01], [0x59, 0x04] + DTC_Number, 0, iStep)
                iStep,uds_resp,size = Driver['CAN'].checkReqResp(Driver, [0x19, 0x06] + DTC_Number + [0x01], [0x59, 0x06] + DTC_Number, 0, iStep)
            else:
                with assume: assert a == False
            iStep +=1
        return iStep

    '''设置信号值'''
    def setSignalValue(Self, Driver, message, signal, value, iStep):
        with allure.step(f'Step{iStep}: set the value of the {signal} in {message} to {value}'): # type: ignore
            Driver['CAN'].write_db_signal('Vehicle_CAN','BCM', message, signal, value)
            time.sleep(0.2)
            iStep += 1
            readValue = Driver['CAN'].read_db_signal('Vehicle_CAN','BCM', message, signal)
        return iStep
            

    def checkDTC(self, Driver, DTCNumber, message, cycleTime, missCount, iStep, signal='', faultValue=0, normalValue=0, settype='enable', DtcExist = True):
        req = [0x19,0x02,0x09]

        DTC_Number_Exist = DTCNumber + [0x09]
        DTC_Number_NoExist = DTCNumber + [0x08]
        

        iStep,uds_resp, size = Driver['CAN'].checkReqResp(Driver, '1001', '5001', 0, iStep)
        iStep = self.clearDTC(Driver, iStep)
        time.sleep(1.5)

        if settype == 'enable':
            # 停发报文
            iStep = Driver['CAN'].setSignalEanble(message, False, iStep, 'VIU0')
        else:
            iStep = Driver['CAN'].setSignalValue(message, signal, faultValue, iStep)
        # 查询故障是否已经产生
        iStep = self.searchDTC(Driver, req, DTC_Number_Exist, iStep, DtcExist, cycleTime * missCount + 100)

        if settype == 'enable':
            # 停发报文
            iStep = Driver['CAN'].setSignalEanble(message, True, iStep, 'VIU0')
        else:
            iStep = Driver['CAN'].setSignalValue(message, signal, normalValue, iStep)
        # 查询故障是否已经恢复
        iStep = self.searchDTC(Driver, req, DTC_Number_NoExist, iStep, DtcExist, cycleTime * missCount + 100, 1)
        # 重启产品
        iStep = Driver['CAN'].restartETC_Power(Driver,iStep)

        iStep = self.searchDTC(Driver, req, DTC_Number_NoExist, iStep, DtcExist, 1000, 1)
        iStep = self.clearDTC(Driver, iStep)
        iStep = self.searchDTC(Driver, req, DTC_Number_NoExist, iStep, False, 1000, 1)

        return iStep


    @allure.title('测试蓄电池电压过低产生低压故障')
    @allure.description('测试物理寻址默认会话下蓄电池电压过低产生低压故障')
    def test_LowCircuitVoltage_DTC_Default_Phy(self,action,Driver):
        req = [0x19,0x02,0x09]
        DTC_Number = [0x91,0x17,0x16]


        iStep = 1
        iStep,uds_resp, size = Driver['CAN'].checkReqResp(Driver, '1001', '5001', 0, iStep)
        iStep = self.clearDTC(Driver, iStep)
        # 设置电压为8v 制造低压故障
        iStep = Driver['CAN'].SetVolt(Driver, 8.3, 3, iStep)
        # 查询低压故障是否已经产生
        iStep = self.searchDTC(Driver, req, DTC_Number + [0x09], iStep, True, 1000)
        # 恢复电压为13v
        iStep = Driver['CAN'].SetVolt(Driver, 12, 1, iStep)
        # 查询故障是否已经恢复
        iStep = self.searchDTC(Driver, req, DTC_Number + [0x08], iStep, True, 1000)

        # 重启产品
        iStep = Driver['CAN'].restartETC_Power(Driver,iStep)

        iStep = self.searchDTC(Driver, req, DTC_Number + [0x08], iStep, True, 1000, 1)
        iStep = self.clearDTC(Driver, iStep)
        iStep = self.searchDTC(Driver, req, DTC_Number + [0x08], iStep, False, 1000, 1)

    @allure.title('蓄电池电压过低产生低压故障边界测试')
    @allure.description('蓄电池电压过低产生低压故障边界测试')
    def test_LowCircuitVoltage_boundary_DTC_Phy(self,action,Driver):

        req = [0x19,0x02,0x09]
        DTC_Number = [0x91,0x17,0x16]

        iStep = 1
        iStep,uds_resp, size = Driver['CAN'].checkReqResp(Driver, '1001', '5001', 0, iStep)

        # 无故障到有故障
        for i in range(6):
            voltage = round(9.3 - i/10, 2)
            
            iStep = Driver['CAN'].SetVolt(Driver, 12, 1, iStep)
            iStep = self.clearDTC(Driver, iStep)
            time.sleep(0.5)
            iStep = Driver['CAN'].SetVolt(Driver, voltage, 1, iStep)
            if voltage < 9:
                iStep = self.searchDTC(Driver, req, DTC_Number + [0x09], iStep, True, 1000)
        
        # 有故障到无故障
        for i in range(6):
            voltage = round(8.7 + i/10, 2)
            iStep = Driver['CAN'].SetVolt(Driver, 12, 1, iStep)
            iStep = self.clearDTC(Driver, iStep)
            time.sleep(1)
            iStep = Driver['CAN'].SetVolt(Driver, voltage, 1, iStep)
            if voltage < 9:
                iStep = self.searchDTC(Driver, req, DTC_Number + [0x09], iStep, True, 1000)
        
        iStep = Driver['CAN'].SetVolt(Driver, 12, 1, iStep)
        iStep = self.clearDTC(Driver, iStep)

    @allure.title('蓄电池电压过高产生高压故障')
    @allure.description('蓄电池电压过高产生高压故障')
    def test_HighCircuitVoltage_DTC_Default_Phy(self,action,Driver):
        req = [0x19,0x02,0x09]
        DTC_Number = [0x91,0x17,0x17]

        iStep = 1
        iStep,uds_resp, size = Driver['CAN'].checkReqResp(Driver, '1001', '5001', 0, iStep)
        iStep = self.clearDTC(Driver, iStep)
        # 设置电压为18v 制造高压故障
        iStep = Driver['CAN'].SetVolt(Driver, 18, 3, iStep)
        # 查询高压故障是否已经产生
        iStep = self.searchDTC(Driver, req, DTC_Number + [0x09], iStep, True, 1000)
        # 恢复电压为12v
        iStep = Driver['CAN'].SetVolt(Driver, 12, 3, iStep)
        # 查询故障是否已经恢复
        iStep = self.searchDTC(Driver, req, DTC_Number + [0x08], iStep, True, 1000)

        # 重启产品
        iStep = Driver['CAN'].restartETC_Power(Driver,iStep)

        iStep = self.searchDTC(Driver, req, DTC_Number + [0x08], iStep, True, 1000, 1)
        iStep = self.clearDTC(Driver, iStep)
        iStep = self.searchDTC(Driver, req, DTC_Number + [0x08], iStep, False, 1000, 1)

    @allure.title('蓄电池电压过高产生高压故障边界测试')
    @allure.description('蓄电池电压过高产生高压故障边界测试')
    def test_HighCircuitVoltage_boundary_DTC_Phy(self,action,Driver):

        req = [0x19,0x02,0x09]
        DTC_Number = [0x91,0x17,0x17]

        iStep = 1
        iStep,uds_resp, size = Driver['CAN'].checkReqResp(Driver, '1001', '5001', 0, iStep)

        # 无故障到有故障
        for i in range(6):
            voltage = round(15.7 + i/10, 2)
            
            iStep = Driver['CAN'].SetVolt(Driver, 12, 1, iStep)
            iStep = self.clearDTC(Driver, iStep)
            time.sleep(0.5)
            iStep = Driver['CAN'].SetVolt(Driver, voltage, 1, iStep)
            if voltage > 17.6:
                iStep = self.searchDTC(Driver, req, DTC_Number + [0x09], iStep, True, 1000)
        
        # 有故障到无故障
        for i in range(6):
            voltage = round(18.3 - i/10, 2)
            iStep = Driver['CAN'].SetVolt(Driver, 12, 1, iStep)
            iStep = self.clearDTC(Driver, iStep)
            time.sleep(1)
            iStep = Driver['CAN'].SetVolt(Driver, voltage, 1, iStep)
            if voltage > 17.6:
                iStep = self.searchDTC(Driver, req, DTC_Number + [0x09], iStep, True, 1000)
        
        iStep = Driver['CAN'].SetVolt(Driver, 12, 1, iStep)
        iStep = self.clearDTC(Driver, iStep)

    @allure.title('CAN BUS OFF测试')
    @allure.description('CAN BUS OFF测试')
    def test_BUSOFF_DTC_Default_Phy(self,action,Driver):
        req = [0x19, 0x02, 0x09]
        DTC_Number = [0xC0,0x77,0x88]

        iStep = 1
        iStep,uds_resp, size = Driver['CAN'].checkReqResp(Driver, '1001', '5001', 0, iStep)
        iStep = self.clearDTC(Driver, iStep)
        # 联通继电器端口6，制造CAN/CANFD I Bus Off故障
        iStep = Driver['CAN'].SetJiDianQiStatus(Driver, iStep, 6, 1)
        time.sleep(1)
        # 断开继电器端口6，恢复CAN/CANFD I Bus Off故障
        iStep = Driver['CAN'].SetJiDianQiStatus(Driver, iStep, 6, 0)
        time.sleep(1)
        # 查询BUSSOFF故障是否已经产生并恢复
        iStep = self.searchDTC(Driver, req, DTC_Number + [0x08], iStep, True, 1000)

        # 重启产品
        iStep = Driver['CAN'].restartETC_Power(Driver,iStep)

        iStep = self.searchDTC(Driver, req, DTC_Number + [0x08], iStep, True, 1000, 1)
        iStep = self.clearDTC(Driver, iStep)
        iStep = self.searchDTC(Driver, req, DTC_Number + [0x08], iStep, False, 1000, 1)

    
    # @allure.title('VIU节点VIU_Route_Body3丢失测试')
    # @allure.description('VIU节点VIU_Route_Body3丢失测试')
    # def test_VIU_DTC_0x340_Default_Phy(self,action,Driver):
    #     req = [0x19, 0x02, 0x09]
    #     DTC_Number = [0xC2,0x6C,0x87]
    #     iStep = 1
    #     self.checkDTC(Driver, DTC_Number, 'VIU_Route_Body3', 1000, 20, iStep)

    @allure.title('VIU节点TBOX_TimeInfo_VV03丢失测试')
    @allure.description('VIU节点TBOX_TimeInfo_VV03丢失测试')
    def test_VIU_DTC_0x5E2_Default_Phy(self,action,Driver):
        req = [0x19, 0x02, 0x09]
        DTC_Number = [0xC2,0x6C,0x87]
        iStep = 1
        self.checkDTC(Driver, DTC_Number, 'TBOX_TimeInfo_VV03', 1000, 10, iStep)
